/*   The MIT License
 *   
 *   Carina Engine
 *   Copyright (c) 2009 2010 2011 2012 Zdravko Velinov
 *   
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#include "carina/world-manager.hh"

namespace Carina
{
Model::Model(const MeshPtr& mesh, const AABBHierarchyPtr& colh)
    :   m_Mesh(mesh),
        m_ColH(colh)
{
}

MeshPtr Model::getMesh()
{
    return m_Mesh;
}

AABBHierarchyPtr Model::getBVH()
{
    return m_ColH;
}

ModelInstance::ModelInstance(const ModelPtr& mod)
    :   m_Model(mod)
{
    AABBHierarchyPtr h = mod->getBVH();
    if(h.get())
        m_CLCache = *h;
}

void ModelInstance::update(const Matrix4& mat)
{
    AABBHierarchy* bvh = m_Model->getBVH().get();
    if(bvh)
        m_CLCache._update(*bvh, mat);
}

bool ModelInstance::testCollision(const ModelInstance& model_inst) const
{
    return false;
}

bool ModelInstance::testIntersection(const Ray& ray) const
{
    return m_CLCache.testIntersection(ray);
}

bool ModelInstance::testIntersection(const Segment& seg) const
{
    return m_CLCache.testIntersection(seg);
}

ModelNode::ModelNode(const WorldManagerPtr& wmgr, const ModelInstancePtr& model)
    :   m_ModelInst(model),
        m_WorldManager(wmgr)
{
    m_WorldManager->attachModelInstance(m_ModelInst);
}

ModelNode::~ModelNode()
{
    m_WorldManager->detachModelInstance(m_ModelInst);
}

void ModelNode::draw(Scene& scene)
{
    m_ModelInst->getModel()->getMesh()->draw(scene, m_WorldMatrix);
}

void ModelNode::notifyTransform(const Matrix4& wmat)
{
    m_WorldMatrix = wmat;
    m_ModelInst->update(m_WorldMatrix);
}

ConfigurablePtr ModelNode::getConfigurable()
{
    return shared_from_this();
}

ConfigurableConstPtr ModelNode::getConfigurable() const
{
    return shared_from_this();
}

WorldManager::WorldManager()
{
}

WorldManager::~WorldManager()
{
}

ModelPtr WorldManager::getModel(const string& model_name)
{
    ModelMap::iterator iter = m_ModelCache.find(model_name);
    if(iter == m_ModelCache.end())
        THROW_EXCEPTION("Unknown model");
    return iter->second;
}

void WorldManager::clear()
{
    m_ModelCache.clear();
}

void WorldManager::attachModelInstance(const ModelInstancePtr& model_inst)
{
    m_ModelArray.push_back(model_inst);
}

void WorldManager::detachModelInstance(const ModelInstancePtr& model_inst)
{
    for(size_t i = 0; i < m_ModelArray.size(); ++i)
        if(model_inst == m_ModelArray[i])
        {
            m_ModelArray.erase(i);
            return;
        }
    assert(false);
}

void WorldManager::registerModel(const string& name, const ModelPtr& model)
{
    m_ModelCache[name] = model;
}

void WorldManager::unregisterModel(const string& model_name)
{
    ModelMap::iterator iter = m_ModelCache.find(model_name);
    if(iter != m_ModelCache.end())
        m_ModelCache.erase(iter);
}

void WorldManager::rayQuery(const Ray& ray, DataVector<ModelInstancePtr>& col)
{
    for(size_t i = 0; i < m_ModelArray.size(); ++i)
        if(m_ModelArray[i]->testIntersection(ray))
            col.push_back(m_ModelArray[i]);
}

void WorldManager::segmentQuery(const Segment& seg, DataVector<ModelInstancePtr>& col)
{
    for(size_t i = 0; i < m_ModelArray.size(); ++i)
        if(m_ModelArray[i]->testIntersection(seg))
            col.push_back(m_ModelArray[i]);
}
}
